<!DOCTYPE HTML>
<html lang="en">


<head>
    <meta charset="utf-8">
    <meta name="keywords" content="Eggjs,Webpack,easywebpack,Egg React SSR,Egg Vue SSR, web ide, Flutter, React Native,Egg/前端工程/多端技术-Webpack 热更新实现原理分析, 前端工程化, node, egg, webpack, easywebpack, ves, res, ssr, react server side render, vue server side render">
    <meta name="description" content="Egg/前端工程/多端技术,概述在使用 Webpack 构建开发期时，Webpack 提供热更新功能为开发带来良好的体验和开发效率。Webpack 热更新可以做到页面无刷新局部更新能力。Webpack 热更新机制相比传统的直接自动刷新浏览器，可以保持元素当前状态，特别是在编写 css 样式调整布局的时候优势明显，不用反复...">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="renderer" content="webkit|ie-stand|ie-comp">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <title>Egg/前端工程/多端技术-Webpack 热更新实现原理分析 | easy-team</title>
    <link rel="icon" type="image/png" href="/favicon.png">

    <link rel="stylesheet" type="text/css" href="/libs/awesome/css/all.css">
    <link rel="stylesheet" type="text/css" href="/libs/materialize/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/css/my.css">

    <script src="/libs/jquery/jquery-2.2.0.min.js"></script>
    <script src="/libs/materialize/materialize.min.js"></script>
    <script src="/libs/materialize/materialize.min.js"></script>
    <script src="/libs/masonry/masonry.pkgd.min.js"></script>
    <script src="/libs/aos/aos.js"></script>
    <script src="/libs/scrollprogress/scrollProgress.min.js"></script>
    <script src="/libs/lightGallery/js/lightgallery-all.min.js"></script>
    <script src="/js/matery.js"></script>
<link rel="stylesheet" href="/css/prism-tomorrow.css" type="text/css"></head>


<body>
    <header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="nav-wrapper head-container">
            <div class="brand-logo">
                <a href="/" class="waves-effect waves-light">
                    
                    <img src="/medias/logo.png" class="logo-img" alt="LOGO">
                    
                    <span class="logo-span">easy-team</span>
                </a>
            </div>
            

<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/" class="waves-effect waves-light">
      
      <i class="fas fa-home" style="zoom: 0.6;"></i>
      
      <span>Home</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/res" class="waves-effect waves-light">
      
      <i class="fas fa-forward" style="zoom: 0.6;"></i>
      
      <span>Res</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/ves" class="waves-effect waves-light">
      
      <i class="fas fa-forward" style="zoom: 0.6;"></i>
      
      <span>Ves</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="" class="waves-effect waves-light">

      
      <i class="fas fa-list" style="zoom: 0.6;"></i>
      
      <span>Egg</span>
      <i class="fas fa-chevron-down" aria-hidden="true" style="zoom: 0.6;"></i>
    </a>
    <ul class="sub-nav menus_item_child ">
      
      <li>
        <a href="/egg-react">
          
          <i class="fas fa-forward" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>Egg+React</span>
        </a>
      </li>
      
      <li>
        <a href="/egg-vue">
          
          <i class="fas fa-forward" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>Egg+Vue</span>
        </a>
      </li>
      
    </ul>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/easywebpack" class="waves-effect waves-light">
      
      <i class="fas fa-building" style="zoom: 0.6;"></i>
      
      <span>Webpack</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/blog" class="waves-effect waves-light">
      
      <i class="fas fa-hacker-news" style="zoom: 0.6;"></i>
      
      <span>Blog</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/about" class="waves-effect waves-light">
      
      <i class="fas fa-user-circle" style="zoom: 0.6;"></i>
      
      <span>About</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/contact" class="waves-effect waves-light">
      
      <i class="fas fa-comments" style="zoom: 0.6;"></i>
      
      <span>Contact</span>
    </a>
    
  </li>
  
  <li>
    <a href="#searchModal" class="modal-trigger waves-effect waves-light">
      <i id="searchIcon" class="fas fa-search" title="Search" style="zoom: 0.85;"></i>
    </a>
  </li>
</ul>

<div id="mobile-nav" class="side-nav sidenav">

    <div class="mobile-head bg-color">
        
        <img src="/medias/logo.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">easy-team</div>
        <div class="logo-desc">
            
            Node, 前端工程化, 多端技术
            
        </div>
    </div>

    

    <ul class="menu-list mobile-menu-list">
        
        <li class="m-nav-item">
	  
		<a href="/" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-home"></i>
			
			Home
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/res" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-forward"></i>
			
			Res
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/ves" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-forward"></i>
			
			Ves
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="javascript:;">
			
				<i class="fa-fw fas fa-list"></i>
			
			Egg
			<span class="m-icon"><i class="fas fa-chevron-right"></i></span>
		</a>
            <ul  style="background:  ;" >
              
                <li>   
				
                  <a href="/egg-react " style="margin-left:75px";>
				  
				   <i class="fa fas fa-forward" style="position: absolute;left:50px" ></i>
			      
		          <span>Egg+React</span>
                  </a>
                </li>
              
                <li>   
				
                  <a href="/egg-vue " style="margin-left:75px";>
				  
				   <i class="fa fas fa-forward" style="position: absolute;left:50px" ></i>
			      
		          <span>Egg+Vue</span>
                  </a>
                </li>
               
            </ul>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/easywebpack" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-building"></i>
			
			Webpack
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/blog" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-hacker-news"></i>
			
			Blog
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/about" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-user-circle"></i>
			
			About
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/contact" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-comments"></i>
			
			Contact
		</a>
          
        </li>
        
        
        <li><div class="divider"></div></li>
        <li>
            <a href="https://github.com/easy-team" class="waves-effect waves-light" target="_blank">
                <i class="fab fa-github-square fa-fw"></i>Fork Me
            </a>
        </li>
        
    </ul>
</div>


        </div>

        
            <style>
    .nav-transparent .github-corner {
        display: none !important;
    }

    .github-corner {
        position: absolute;
        z-index: 10;
        top: 0;
        right: 0;
        border: 0;
        transform: scale(1.1);
    }

    .github-corner svg {
        color: #0f9d58;
        fill: #fff;
        height: 64px;
        width: 64px;
    }

    .github-corner:hover .octo-arm {
        animation: a 0.56s ease-in-out;
    }

    .github-corner .octo-arm {
        animation: none;
    }

    @keyframes a {
        0%,
        to {
            transform: rotate(0);
        }
        20%,
        60% {
            transform: rotate(-25deg);
        }
        40%,
        80% {
            transform: rotate(10deg);
        }
    }
</style>

<a href="https://github.com/easy-team" class="github-corner tooltipped hide-on-med-and-down" target="_blank"
   data-tooltip="Fork Me" data-position="left" data-delay="50">
    <svg viewBox="0 0 250 250" aria-hidden="true">
        <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
        <path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
              fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
        <path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
              fill="currentColor" class="octo-body"></path>
    </svg>
</a>
        
    </nav>

</header>

    <div class="bg-cover pd-header" style="height: 9vh">
    <div class="container"></div>
</div>


    <script>
        // 每天切换 banner 图.  Switch banner image every day.
        $('.bg-cover').css('background-image', 'url(/medias/banner/' + new Date().getDay() + '.jpg)');
    </script>
    

<div class="doc-content">
    
     <div class="doc-left-nav" style="width:400px">
        <div class="side-nav docsidenav" style="width:400px">
    <div class="doc-left-nav-title">blog</div>
    <ul class="menu-list mobile-menu-list">
        
            
                <li class="m-nav-item">
                    <a href="/blog/cdal6o" class="waves-effect waves-light" id="m-nav-item-blog-cdal6o">
                        
                        <i class="fas fa-fw fa-link"></i>
                        
                        Egg + Web 自定义前端渲染
                    </a>
                </li>
            
        
            
                <li class="m-nav-item">
                    <a href="/blog/ruv09m" class="waves-effect waves-light" id="m-nav-item-blog-ruv09m">
                        
                        <i class="fas fa-fw fa-link"></i>
                        
                        Egg SSR Webpack 内存编译热更新实现
                    </a>
                </li>
            
        
            
                <li class="m-nav-item">
                    <a href="/blog/sg8avg" class="waves-effect waves-light" id="m-nav-item-blog-sg8avg">
                        
                        <i class="fas fa-fw fa-link"></i>
                        
                        Webpack Babel SSR 构建优化
                    </a>
                </li>
            
        
            
                <li class="m-nav-item">
                    <a href="/blog/zfri65" class="waves-effect waves-light" id="m-nav-item-blog-zfri65">
                        
                        <i class="fas fa-fw fa-link"></i>
                        
                        Webpack 构建大小分析与优化
                    </a>
                </li>
            
        
            
                <li class="m-nav-item">
                    <a href="/blog/xriz8a" class="waves-effect waves-light" id="m-nav-item-blog-xriz8a">
                        
                        <i class="fas fa-fw fa-link"></i>
                        
                        Egg + Vue/Nunjucks 服务端渲染性能测试
                    </a>
                </li>
            
        
            
                <li class="m-nav-item">
                    <a href="/blog/qwr449" class="waves-effect waves-light" id="m-nav-item-blog-qwr449">
                        
                        <i class="fas fa-fw fa-link"></i>
                        
                        Webpack 热更新实现原理分析
                    </a>
                </li>
            
        
            
                <li class="m-nav-item">
                    <a href="/blog/dxrrhq" class="waves-effect waves-light" id="m-nav-item-blog-dxrrhq">
                        
                        <i class="fas fa-fw fa-link"></i>
                        
                        Webpack 4 编译内存泄漏
                    </a>
                </li>
            
        
            
                <li class="m-nav-item">
                    <a href="/blog/kiygy5" class="waves-effect waves-light" id="m-nav-item-blog-kiygy5">
                        
                        <i class="fas fa-fw fa-link"></i>
                        
                        Vue SSR 组件加载
                    </a>
                </li>
            
        
            
                <li class="m-nav-item">
                    <a href="/blog/ekvirc" class="waves-effect waves-light" id="m-nav-item-blog-ekvirc">
                        
                        <i class="fas fa-fw fa-link"></i>
                        
                        Webpack 构建流程解密
                    </a>
                </li>
            
        
            
                <li class="m-nav-item">
                    <a href="/blog/az519d" class="waves-effect waves-light" id="m-nav-item-blog-az519d">
                        
                        <i class="fas fa-fw fa-link"></i>
                        
                        页面资源加载
                    </a>
                </li>
            
        
            
                <li class="m-nav-item m-nav-show">
                    <a href="javascript:;">
                        <i class="fas fa-fw fa-link"></i>
                        Flutter 技术专题
                        <span class="m-icon"><i class="fas fa-chevron-right"></i></span>
                    </a>
                    <ul  style="display:block; background:  ;" >
                        
                            <li>   
                                <a id="m-nav-item-blog-lsrssy" href="/blog/lsrssy " 
                                    style="padding-left:75px";>
                                
                                <span>Flutter  Android  混合工程实践</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-zs3hog" href="/blog/zs3hog " 
                                    style="padding-left:75px";>
                                
                                <span>Flutter iOS 混合工程实践</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-nrswgq" href="/blog/nrswgq " 
                                    style="padding-left:75px";>
                                
                                <span>Android 与 Flutter 入门开发实践指引</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-wydpi1" href="/blog/wydpi1 " 
                                    style="padding-left:75px";>
                                
                                <span>Flutter 动态更新</span>
                                </a>
                            </li>
                         
                    </ul>
                </li>
            
        
            
                <li class="m-nav-item m-nav-show">
                    <a href="javascript:;">
                        <i class="fas fa-fw fa-link"></i>
                        React Native 技术专题
                        <span class="m-icon"><i class="fas fa-chevron-right"></i></span>
                    </a>
                    <ul  style="display:block; background:  ;" >
                        
                            <li>   
                                <a id="m-nav-item-blog-xcw19a" href="/blog/xcw19a " 
                                    style="padding-left:75px";>
                                
                                <span>React Native, Hybrid App, H5 简单对比分析</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-vzgcgb" href="/blog/vzgcgb " 
                                    style="padding-left:75px";>
                                
                                <span>React Native Android APK包大小分析</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-wk5ney" href="/blog/wk5ney " 
                                    style="padding-left:75px";>
                                
                                <span>React Native 自定义插件</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-kghphs" href="/blog/kghphs " 
                                    style="padding-left:75px";>
                                
                                <span>React Native代码执行跟踪和调试</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-qwywww" href="/blog/qwywww " 
                                    style="padding-left:75px";>
                                
                                <span>React Native 热更新实现</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-rh45zo" href="/blog/rh45zo " 
                                    style="padding-left:75px";>
                                
                                <span>React Native Android源码解读和交互原理分析</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-uhmdkg" href="/blog/uhmdkg " 
                                    style="padding-left:75px";>
                                
                                <span>Hybrid系列-phonegap android框架详解</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-sxys82" href="/blog/sxys82 " 
                                    style="padding-left:75px";>
                                
                                <span>Hybrid系列-Cordova android框架详解</span>
                                </a>
                            </li>
                         
                    </ul>
                </li>
            
        
            
                <li class="m-nav-item m-nav-show">
                    <a href="javascript:;">
                        <i class="fas fa-fw fa-link"></i>
                        开发工具使用
                        <span class="m-icon"><i class="fas fa-chevron-right"></i></span>
                    </a>
                    <ul  style="display:block; background:  ;" >
                        
                            <li>   
                                <a id="m-nav-item-blog-syh43v" href="/blog/syh43v " 
                                    style="padding-left:75px";>
                                
                                <span>npm 常用命令</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-vfhw62" href="/blog/vfhw62 " 
                                    style="padding-left:75px";>
                                
                                <span>Git 使用技巧</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-gayu0o" href="/blog/gayu0o " 
                                    style="padding-left:75px";>
                                
                                <span>Docker 随记</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-egret" href="/blog/egret " 
                                    style="padding-left:75px";>
                                
                                <span>Egret  游戏开发</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-og9ogk" href="/blog/og9ogk " 
                                    style="padding-left:75px";>
                                
                                <span>vscode 插件开发</span>
                                </a>
                            </li>
                        
                            <li>   
                                <a id="m-nav-item-blog-qz7n03" href="/blog/qz7n03 " 
                                    style="padding-left:75px";>
                                
                                <span>Mac PHP 环境</span>
                                </a>
                            </li>
                         
                    </ul>
                </li>
            
        
        <li><div class="divider"></div></li>
        
        <li>
            <a href="https://yuque.com/hubcarl" class="waves-effect waves-light" target="_blank">
                <i class="fas fa-fw fa-link"></i>语雀文档
            </a>
        </li>
        <li>
            <a href="https://github.com/hubcarl" class="waves-effect waves-light" target="_blank">
                <i class="fab fa-github-square fa-fw"></i>Github
            </a>
        </li>
    </ul>
</div>

     </div>
    
    <div class="doc-right-nav">
        <div class="card" class="doc-right-card">
            <div class="card-content article-info">
                <h1 style="margin-top: 12px; text-align: center;">Webpack 热更新实现原理分析</h1>
                
                <div class="post-info">
                    <div class="post-date info-break-policy">
                        <i class="far fa-calendar-minus fa-fw"></i>Date:&nbsp;&nbsp;
                        2019-10-20
                    </div>
    
                    
                        
                        <div class="info-break-policy">
                            <i class="far fa-file-word fa-fw"></i>Word Count:&nbsp;&nbsp;
                            2.6k
                        </div>
                        
    
                        
                        <div class="info-break-policy">
                            <i class="far fa-clock fa-fw"></i>Read Times:&nbsp;&nbsp;
                            11 Min
                        </div>
                        
                    
                    
                    
                        <div id="busuanzi_container_page_pv" class="info-break-policy">
                            <i class="far fa-eye fa-fw"></i>Read Count:&nbsp;&nbsp;
                            <span id="busuanzi_value_page_pv"></span>
                        </div>
                    
                </div>
            </div>
            <hr class="clearfix" style="margin: 0px 0px;">
            <div class="card-content article-card-content" style="padding-top: 0px;">
                <div id="articleContent">
                    <h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>在使用 Webpack 构建开发期时，Webpack 提供热更新功能为开发带来良好的体验和开发效率。Webpack 热更新可以做到页面无刷新局部更新能力。Webpack 热更新机制相比传统的直接自动刷新浏览器，可以保持元素当前状态，特别是在编写 css 样式调整布局的时候优势明显，不用反复重新定位元素。<br /></p>
<h2 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h2><ul>
<li>Webpack 配置添加 HotModuleReplacementPlugin 插件</li>
</ul>
<pre class=" language-javascript"><code class="language-javascript">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  plugins<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token keyword">new</span> <span class="token class-name">webpack<span class="token punctuation">.</span>HotModuleReplacementPlugin</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span>
</code></pre>
<ul>
<li>Webpack Entry 注入热更新代码</li>
</ul>
<blockquote>
<p><code>webpack-hot-middleware/client</code> 这里注入热更新代码是关键，保证服务端和客户端能够通信。</p>
</blockquote>
<pre class=" language-javascript"><code class="language-javascript"><span class="token comment" spellcheck="true">// webpack.config.js 普通应用配置</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  entry<span class="token punctuation">:</span><span class="token punctuation">{</span>
    app<span class="token punctuation">:</span> <span class="token punctuation">[</span>  
      <span class="token string">'webpack-hot-middleware/client?path=/__webpack_hmr&amp;noInfo=false&amp;reload=false&amp;quiet=false'</span><span class="token punctuation">,</span>
      <span class="token string">'src/index.js'</span>
    <span class="token punctuation">]</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment" spellcheck="true">// webpack.config.js React应用配置</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  entry<span class="token punctuation">:</span><span class="token punctuation">{</span>
    app<span class="token punctuation">:</span> <span class="token punctuation">[</span>  
      <span class="token string">'react-hot-loader/babel'</span><span class="token punctuation">,</span>
      <span class="token string">'webpack-hot-middleware/client?path=/__webpack_hmr&amp;noInfo=false&amp;reload=false&amp;quiet=false'</span><span class="token punctuation">,</span>
      <span class="token string">'src/index.js'</span>
    <span class="token punctuation">]</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<ul>
<li><p>Node Server 引入 <code>webpack-dev-middlerware</code> 和 <code>webpack-hot-middleware</code> 插件，如果是 koa 引入对应的 <code>koa-webpack-dev-middlerware</code> 和 <code>koa-webpack-hot-middleware</code></p>
<pre class=" language-javascript"><code class="language-javascript"><span class="token keyword">const</span> devMiddleware <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'koa-webpack-dev-middleware'</span><span class="token punctuation">)</span><span class="token punctuation">(</span>compiler<span class="token punctuation">,</span> <span class="token punctuation">{</span>
publicPath<span class="token punctuation">,</span>
 stats<span class="token punctuation">:</span> <span class="token punctuation">{</span>
  colors<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  children<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  modules<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
  chunks<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
  chunkModules<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
watchOptions<span class="token punctuation">:</span> <span class="token punctuation">{</span>
  ignored<span class="token punctuation">:</span> <span class="token regex">/node_modules/</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>devMiddleware<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> hotMiddleware <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'koa-webpack-hot-middleware'</span><span class="token punctuation">)</span><span class="token punctuation">(</span>compiler<span class="token punctuation">,</span> <span class="token punctuation">{</span>
 log<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
 reload<span class="token punctuation">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>hotMiddleware<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
</li>
<li><p>代码入口增加热更新处理（ 以 React 为例，Vue 内置支持，无需额外配置）</p>
</li>
</ul>
<blockquote>
<p>需要安装 react-hot-loader 依赖,  通过 module.hot.accept 接收热更新信息进行组件重新 render</p>
</blockquote>
<pre class=" language-javascript"><code class="language-javascript"><span class="token string">'use strict'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> React<span class="token punctuation">,</span> <span class="token punctuation">{</span> Component <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span>
<span class="token keyword">import</span> ReactDOM <span class="token keyword">from</span> <span class="token string">'react-dom'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> Provider <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react-redux'</span>

<span class="token keyword">import</span> <span class="token punctuation">{</span> AppContainer <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react-hot-loader'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> createStore <span class="token keyword">from</span> <span class="token string">'./store'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> Routes <span class="token keyword">from</span> <span class="token string">'./router'</span>
<span class="token keyword">import</span> <span class="token string">'./app.css'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> App <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> module<span class="token punctuation">.</span>hot <span class="token operator">?</span> <span class="token operator">&lt;</span>AppContainer<span class="token operator">></span><span class="token operator">&lt;</span>Routes <span class="token operator">/</span><span class="token operator">></span><span class="token operator">&lt;</span><span class="token operator">/</span>AppContainer<span class="token operator">></span> <span class="token punctuation">:</span> <span class="token operator">&lt;</span>Routes <span class="token operator">/</span><span class="token operator">></span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> Entry <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">(</span><span class="token operator">&lt;</span>div<span class="token operator">></span>
  <span class="token operator">&lt;</span>Provider store<span class="token operator">=</span><span class="token punctuation">{</span> <span class="token function">createStore</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token operator">></span>
    <span class="token operator">&lt;</span>App <span class="token operator">/</span><span class="token operator">></span>
  <span class="token operator">&lt;</span><span class="token operator">/</span>Provider<span class="token operator">></span>
<span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>

ReactDOM<span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span><span class="token operator">&lt;</span>Entry <span class="token operator">/</span><span class="token operator">></span><span class="token punctuation">,</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'app'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span>module<span class="token punctuation">.</span>hot<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  module<span class="token punctuation">.</span>hot<span class="token punctuation">.</span><span class="token function">accept</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2 id="热更新一探"><a href="#热更新一探" class="headerlink" title="热更新一探"></a>热更新一探</h2><p>首先我们启动 <a href="https://link.zhihu.com/?target=https%3A//github.com/hubcarl/egg-vue-webpack-boilerplate">egg-vue-webpack-boilerplate</a> 应用，通过 chrome-dev-tool 看看首次打开页面前端和后端通信是如何建立的？</p>
<p><img src="https://cdn.nlark.com/yuque/0/2019/jpeg/116733/1560615347762-6826338b-2369-448f-a8bc-23acdd434a19.jpeg#align=left&display=inline&height=140&originHeight=140&originWidth=620&size=0&status=done&width=620" alt=""></p>
<p>有一个<strong>webpack_hmr的请求：<a href="https://link.zhihu.com/?target=http%3A//127.0.0.1%3A9000/">http://127.0.0.1:9000/</a></strong>webpack_hmr.</p>
<p>从这张图里面看到几个信息</p>
<ul>
<li>这里看到内容类型为 eventStream，具体是啥请看之后介绍的 EventSource。</li>
<li>返回类型为message， 内容关键的有个 action: sync 和 hash：73c528ba5b06e7e9ab26， 这个几个信息在后面会用到。 这里的 hash 为 Webpack 初始化的一个hash，在 vendor.js 文件里面可以看到, 每次页面首次加载时，都会重新生成一个。(var hotCurrentHash = “73c528ba5b06e7e9ab26”)</li>
<li>有一个空的message信息，通过观察发现和后面查看代码发现，这个是为了保证后端与客户端通信保持连接，后端隔一段时间会向客户端发送一段信息。</li>
</ul>
<p>然后修改 about.vue 文件保存后，发现控制台 Webpack 马上重新编译了，UI 无刷新更新了。</p>
<p>1.这时候会发现 Webpack 编译结果多了两个update的文件， 而且文件名包含上面的 hash 信息。<br /><code>4.73c528ba5b06e7e9ab26.hot-update.js 2.93 kB [emitted] about&lt;br /&gt;73c528ba5b06e7e9ab26.hot-update.json 43 bytes [emitted]</code></p>
<p>2.同时，chrome-dev-tool 请求面板下多了两个请求，其中 hot-update.json 为 ajax请求， hot-update.js 为 GET 请求， 也就是插入 script 链接到文档中的script 请求。<br /><br><br /><img src="https://cdn.nlark.com/yuque/0/2019/jpeg/116733/1560615348143-6225ace9-8ac5-476e-9dfe-181e8a4a522d.jpeg#align=left&display=inline&height=128&originHeight=128&originWidth=658&size=0&status=done&width=658" alt=""></p>
<p>3.页面内容插入了 <code>4.73c528ba5b06e7e9ab26.hot-update.js</code> script文件</p>
<p><img src="https://cdn.nlark.com/yuque/0/2019/jpeg/116733/1560615347851-6b5fa30d-3009-436e-8224-1e455e05a39b.jpeg#align=left&display=inline&height=195&originHeight=195&originWidth=706&size=0&status=done&width=706" alt=""></p>
<p>4.我们来初步看一下两个文件的内容：</p>
<ul>
<li>4.73c528ba5b06e7e9ab26.hot-update.js</li>
</ul>
<p><code>{&quot;h&quot;:&quot;540f0a679c8bcbf12848&quot;,&quot;c&quot;:{&quot;4&quot;:true}}</code></p>
<ul>
<li>73c528ba5b06e7e9ab26.hot-update.json</li>
</ul>
<pre class=" language-javascript"><code class="language-javascript"><span class="token function">webpackHotUpdate</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">,</span><span class="token punctuation">{</span>
 <span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span>module<span class="token punctuation">,</span> __webpack_exports__<span class="token punctuation">,</span> __webpack_require__<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment" spellcheck="true">// ...... 此处为 about.vue 组件代码逻辑 </span><span class="token comment" spellcheck="true">/* hot reload */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">var</span> hotAPI <span class="token operator">=</span> <span class="token function">__webpack_require__</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
        hotAPI<span class="token punctuation">.</span><span class="token function">install</span><span class="token punctuation">(</span><span class="token function">__webpack_require__</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>hotAPI<span class="token punctuation">.</span>compatible<span class="token punctuation">)</span> <span class="token keyword">return</span> module<span class="token punctuation">.</span>hot<span class="token punctuation">.</span><span class="token function">accept</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>module<span class="token punctuation">.</span>hot<span class="token punctuation">.</span>data<span class="token punctuation">)</span> <span class="token punctuation">{</span>
          hotAPI<span class="token punctuation">.</span><span class="token function">createRecord</span><span class="token punctuation">(</span><span class="token string">"data-v-80abbab2"</span><span class="token punctuation">,</span> Component<span class="token punctuation">.</span>options<span class="token punctuation">)</span>
        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
          hotAPI<span class="token punctuation">.</span><span class="token function">reload</span><span class="token punctuation">(</span><span class="token string">"data-v-80abbab2"</span><span class="token punctuation">,</span> Component<span class="token punctuation">.</span>options<span class="token punctuation">)</span>
      <span class="token string">' + '</span>  <span class="token punctuation">}</span>
        module<span class="token punctuation">.</span>hot<span class="token punctuation">.</span><span class="token function">dispose</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token punctuation">{</span>
          disposed <span class="token operator">=</span> <span class="token boolean">true</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span>
 <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>5.进行多次热更新效果</p>
<p><img src="https://cdn.nlark.com/yuque/0/2019/jpeg/116733/1560615347793-b90dc444-b6b9-44e4-8fa0-ebb697a780dc.jpeg#align=left&display=inline&height=487&originHeight=296&originWidth=720&size=0&status=done&width=1184" alt=""></p>
<p>从上面截图可以看到，每次服务端发送的消息(EventStrean) 的 hash 将作为下次 hot-update.json 和 hot-update.js 文件的 hash。<br />结合上面的分析，接下来从实现到代码层面分析一下整个流程。</p>
<h2 id=""><a href="#" class="headerlink" title=""></a></h2><h2 id="热更新实现分析"><a href="#热更新实现分析" class="headerlink" title="热更新实现分析"></a>热更新实现分析</h2><h3 id="EventSource-服务端与客户端通信"><a href="#EventSource-服务端与客户端通信" class="headerlink" title="EventSource 服务端与客户端通信"></a>EventSource 服务端与客户端通信</h3><p><br />首先通过查看代码 <code>webpack-hot-middleware/client</code> 发现通信是用 <code>window.EventSource</code> 实现,那 EventSource 是什么东西呢？<br />EventSource 是 HTML5 中 Server-sent Events 规范的一种技术实现。EventSource 接口用于接收服务器发送的事件。它通过HTTP连接到一个服务器，以text/event-stream 格式接收事件, 不关闭连接。通过 EventSource 服务端可以主动给客户端发现消息，使用的是 HTTP协议，单项通信，只能服务器向浏览器发送； 与 WebSocket 相比轻量，使用简单，支持断线重连。更多信息参考 <a href="https://link.zhihu.com/?target=https%3A//developer.mozilla.org/zh-CN/docs/Server-sent_events/Using_server-sent_events">MDN</a></p>
<h2 id="-1"><a href="#-1" class="headerlink" title=""></a></h2><h3 id="Node-端通信实现"><a href="#Node-端通信实现" class="headerlink" title="Node 端通信实现"></a>Node 端通信实现</h3><blockquote>
<p>Node 通过中间件 <a href="https://link.zhihu.com/?target=https%3A//github.com/glenjamin/webpack-hot-middleware/blob/master/middleware.js">webpack-hot-middleware/middleware.js</a></p>
</blockquote>
<h4 id="创建-createEventStream-流"><a href="#创建-createEventStream-流" class="headerlink" title="创建 createEventStream 流"></a>创建 createEventStream 流</h4><p>首先看一下中间件核心代码，主要是向客户端发送消息</p>
<ul>
<li>compile 发送 编译中 消息给客户端</li>
<li>build 发送 编译完成 消息给客户端</li>
<li>sync 文件修复热更新或者报错会发送该消息<pre class=" language-javascript"><code class="language-javascript"><span class="token comment" spellcheck="true">// 初始化 EventStream 发送消息通道</span>
<span class="token keyword">var</span> eventStream <span class="token operator">=</span> <span class="token punctuation">{</span> 
handler<span class="token punctuation">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span>req<span class="token punctuation">,</span> res<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    req<span class="token punctuation">.</span>socket<span class="token punctuation">.</span><span class="token function">setKeepAlive</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    res<span class="token punctuation">.</span><span class="token function">writeHead</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
      <span class="token string">'Access-Control-Allow-Origin'</span><span class="token punctuation">:</span> <span class="token string">'*'</span><span class="token punctuation">,</span>
      <span class="token string">'Content-Type'</span><span class="token punctuation">:</span> <span class="token string">'text/event-stream;charset=utf-8'</span><span class="token punctuation">,</span>
      <span class="token string">'Cache-Control'</span><span class="token punctuation">:</span> <span class="token string">'no-cache, no-transform'</span><span class="token punctuation">,</span>
      <span class="token string">'Connection'</span><span class="token punctuation">:</span> <span class="token string">'keep-alive'</span><span class="token punctuation">,</span>
      <span class="token string">'X-Accel-Buffering'</span><span class="token punctuation">:</span> <span class="token string">'no'</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    res<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span><span class="token string">'\n'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">var</span> id <span class="token operator">=</span> clientId<span class="token operator">++</span><span class="token punctuation">;</span>
    clients<span class="token punctuation">[</span>id<span class="token punctuation">]</span> <span class="token operator">=</span> res<span class="token punctuation">;</span>
    req<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">"close"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
      <span class="token keyword">delete</span> clients<span class="token punctuation">[</span>id<span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
publish<span class="token punctuation">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span>payload<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token function">everyClient</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span>client<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      client<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span><span class="token string">"data: "</span> <span class="token operator">+</span> JSON<span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>payload<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"\n\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment" spellcheck="true">// 根据 Webpack 编译状态 主动发送消息给客户端</span>
<span class="token keyword">function</span> <span class="token function">webpackHotMiddleware</span><span class="token punctuation">(</span>compiler<span class="token punctuation">,</span> opts<span class="token punctuation">)</span> <span class="token punctuation">{</span>
compiler<span class="token punctuation">.</span><span class="token function">plugin</span><span class="token punctuation">(</span><span class="token string">"compile"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  latestStats <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>opts<span class="token punctuation">.</span>log<span class="token punctuation">)</span> opts<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"webpack building..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  eventStream<span class="token punctuation">.</span><span class="token function">publish</span><span class="token punctuation">(</span><span class="token punctuation">{</span>action<span class="token punctuation">:</span> <span class="token string">"building"</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
compiler<span class="token punctuation">.</span><span class="token function">plugin</span><span class="token punctuation">(</span><span class="token string">"done"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span>statsResult<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment" spellcheck="true">// Keep hold of latest stats so they can be propagated to new clients</span>
  latestStats <span class="token operator">=</span> statsResult<span class="token punctuation">;</span>
  <span class="token comment" spellcheck="true">// 当首次编译完成 和 修改代码重新编译(热更新)完成时发送</span>
  <span class="token function">publishStats</span><span class="token punctuation">(</span><span class="token string">"built"</span><span class="token punctuation">,</span> latestStats<span class="token punctuation">,</span> eventStream<span class="token punctuation">,</span> opts<span class="token punctuation">.</span>log<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> middleware <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>req<span class="token punctuation">,</span> res<span class="token punctuation">,</span> next<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">pathMatch</span><span class="token punctuation">(</span>req<span class="token punctuation">.</span>url<span class="token punctuation">,</span> opts<span class="token punctuation">.</span>path<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment" spellcheck="true">// 见下面的 handler 实现，中间件通过 `req.socket.setKeepAlive` 开启长链接通道, </span>
  eventStream<span class="token punctuation">.</span><span class="token function">handler</span><span class="token punctuation">(</span>req<span class="token punctuation">,</span> res<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>latestStats<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment" spellcheck="true">// 服务端向客户端写入数据，sync 表示告诉客户端热更新已经准备好</span>
     eventStream<span class="token punctuation">.</span><span class="token function">publish</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      name<span class="token punctuation">:</span> stats<span class="token punctuation">.</span>name<span class="token punctuation">,</span>
      action<span class="token punctuation">:</span> <span class="token string">"sync"</span><span class="token punctuation">,</span>
      time<span class="token punctuation">:</span> stats<span class="token punctuation">.</span>time<span class="token punctuation">,</span>
      hash<span class="token punctuation">:</span> stats<span class="token punctuation">.</span>hash<span class="token punctuation">,</span>
      warnings<span class="token punctuation">:</span> stats<span class="token punctuation">.</span>warnings <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
      errors<span class="token punctuation">:</span> stats<span class="token punctuation">.</span>errors <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
      modules<span class="token punctuation">:</span> <span class="token function">buildModuleMap</span><span class="token punctuation">(</span>stats<span class="token punctuation">.</span>modules<span class="token punctuation">)</span>
     <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> middleware<span class="token punctuation">;</span></code></pre>
</li>
</ul>
<h2 id="-2"><a href="#-2" class="headerlink" title=""></a></h2><h3 id="客户端通信实现"><a href="#客户端通信实现" class="headerlink" title="客户端通信实现"></a>客户端通信实现</h3><p>服务端通过 EventSource 发送消息给客户端了，我们来看看客户端的通信实现。打开 <a href="https://link.zhihu.com/?target=https%3A//github.com/glenjamin/webpack-hot-middleware/blob/master/client.js">webpack-hot-middleware/client.js</a> 的代码实现：</p>
<pre class=" language-javascript"><code class="language-javascript"><span class="token keyword">var</span> source <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">window<span class="token punctuation">.</span>EventSource</span><span class="token punctuation">(</span><span class="token string">'(http://127.0.0.1:9000/__webpack_hmr)'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
source<span class="token punctuation">.</span>onopen <span class="token operator">=</span> handleOnline<span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 建立链接</span>
source<span class="token punctuation">.</span>onerror <span class="token operator">=</span> handleDisconnect<span class="token punctuation">;</span>
source<span class="token punctuation">.</span>onmessage <span class="token operator">=</span> handleMessage<span class="token punctuation">;</span> <span class="token comment" spellcheck="true">// 接收服务端消息，然后进行相应处理</span></code></pre>
<p>Node端会主动发送消息给客户端， 客户端 EventSource 关键代码处理消息代码如下：</p>
<pre class=" language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">processMessage</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">switch</span><span class="token punctuation">(</span>obj<span class="token punctuation">.</span>action<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">case</span> <span class="token string">"building"</span><span class="token punctuation">:</span> 
      <span class="token keyword">if</span> <span class="token punctuation">(</span>options<span class="token punctuation">.</span>log<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>
          <span class="token string">"[HMR] bundle "</span> <span class="token operator">+</span> <span class="token punctuation">(</span>obj<span class="token punctuation">.</span>name <span class="token operator">?</span> <span class="token string">"'"</span> <span class="token operator">+</span> obj<span class="token punctuation">.</span>name <span class="token operator">+</span> <span class="token string">"' "</span> <span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">)</span> <span class="token operator">+</span><span class="token string">"rebuilding"</span>
        <span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      <span class="token keyword">break</span><span class="token punctuation">;</span>
    <span class="token keyword">case</span> <span class="token string">"built"</span><span class="token punctuation">:</span> <span class="token comment" spellcheck="true">// 这里没有break，所以 编译完成会执行 build 和 sync 逻辑</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>options<span class="token punctuation">.</span>log<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>
          <span class="token string">"[HMR] bundle "</span> <span class="token operator">+</span> <span class="token punctuation">(</span>obj<span class="token punctuation">.</span>name <span class="token operator">?</span> <span class="token string">"'"</span> <span class="token operator">+</span> obj<span class="token punctuation">.</span>name <span class="token operator">+</span> <span class="token string">"' "</span> <span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">)</span> <span class="token operator">+</span>
          <span class="token string">"rebuilt in "</span> <span class="token operator">+</span> obj<span class="token punctuation">.</span>time <span class="token operator">+</span> <span class="token string">"ms"</span>
        <span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      <span class="token comment" spellcheck="true">// fall through</span>
    <span class="token keyword">case</span> <span class="token string">"sync"</span><span class="token punctuation">:</span>
      <span class="token function">processUpdate</span><span class="token punctuation">(</span>obj<span class="token punctuation">.</span>hash<span class="token punctuation">,</span> obj<span class="token punctuation">.</span>modules<span class="token punctuation">,</span> options<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">break</span><span class="token punctuation">;</span>
    <span class="token keyword">default</span><span class="token punctuation">:</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>customHandler<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token function">customHandler</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
上面 building<span class="token punctuation">,</span> built<span class="token punctuation">,</span> sync 三种消息于服务端发送的消息对应， 这样就完成了服务端和客户端通信。
因 build 的 action 时， build <span class="token keyword">case</span> 没有 <span class="token keyword">break</span>，所以当修改文件时，编译完成发送 build 消息时，会依次执行 build 和 sync 逻辑， 也就是进入 processUpdate 流程。processUpdate <span class="token function">接收到信息</span><span class="token punctuation">(</span> hash<span class="token punctuation">,</span> module<span class="token punctuation">)</span> 之后， 进入 module<span class="token punctuation">.</span>hot<span class="token punctuation">.</span>check 和 module<span class="token punctuation">.</span>hot<span class="token punctuation">.</span>apply 流程。</code></pre>
<h2 id="客户端热更新"><a href="#客户端热更新" class="headerlink" title="客户端热更新"></a>客户端热更新</h2><p>首先我们再来看看 <code>module.hot</code> 初始化实现逻辑</p>
<h2 id="module-hot-初始化"><a href="#module-hot-初始化" class="headerlink" title="module.hot 初始化"></a>module.hot 初始化</h2><p><strong>webpack_require</strong> 函数定义时，通过 hotCreateModule 为每个 module 初始化 hot 逻辑</p>
<pre class=" language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">__webpack_require__</span><span class="token punctuation">(</span>moduleId<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">var</span> module <span class="token operator">=</span> installedModules<span class="token punctuation">[</span>moduleId<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
    i<span class="token punctuation">:</span> moduleId<span class="token punctuation">,</span>
    l<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
    exports<span class="token punctuation">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
    hot<span class="token punctuation">:</span> <span class="token function">hotCreateModule</span><span class="token punctuation">(</span>moduleId<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment" spellcheck="true">// 前端通过 ajax 获取热更新文件内容</span>
    parents<span class="token punctuation">:</span>xxxx<span class="token punctuation">,</span>
    children<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> module<span class="token punctuation">.</span>exports<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p><strong>hotCreateModule 实现</strong></p>
<pre class=" language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">hotCreateModule</span><span class="token punctuation">(</span>moduleId<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">var</span> hot <span class="token operator">=</span> <span class="token punctuation">{</span>
    accept<span class="token punctuation">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span>dep<span class="token punctuation">,</span> callback<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    check<span class="token punctuation">:</span> hotCheck<span class="token punctuation">,</span>
    apply<span class="token punctuation">:</span> hotApply<span class="token punctuation">,</span>
    status<span class="token punctuation">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span>l<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token operator">...</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> hot<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2 id="-3"><a href="#-3" class="headerlink" title=""></a></h2><h2 id="hotCheck：前端通过-ajax-获取热更新文件内容"><a href="#hotCheck：前端通过-ajax-获取热更新文件内容" class="headerlink" title="hotCheck：前端通过 ajax 获取热更新文件内容"></a>hotCheck：前端通过 ajax 获取热更新文件内容</h2><p>从 <strong>热更新一探</strong>：[进行多次热更新效果] 上面截图可以看到，每次服务端发送的消息(EventStrean) 的 hash 将作为下次 hot-update.json 和 hot-update.js 文件的 hash。也就是下面客户端更新当前<br />hotCurrentHash 值，作为下次的 hot-update.json 和 hot-update.js 更新请求。</p>
<pre class=" language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">hotCheck</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> reject<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">var</span> __webpack_require__<span class="token punctuation">.</span>p <span class="token operator">+</span> <span class="token string">""</span> <span class="token operator">+</span> hotCurrentHash <span class="token operator">+</span> <span class="token string">".hot-update.json"</span><span class="token punctuation">;</span>
        <span class="token keyword">var</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        request<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">"GET"</span><span class="token punctuation">,</span> requestPath<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        request<span class="token punctuation">.</span>timeout <span class="token operator">=</span> requestTimeout<span class="token punctuation">;</span>
        request<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        request<span class="token punctuation">.</span>onreadystatechange <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token keyword">if</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span>readyState <span class="token operator">===</span> <span class="token number">4</span> <span class="token operator">&amp;&amp;</span> request<span class="token punctuation">.</span>status <span class="token operator">===</span> <span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
            <span class="token function">reject</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">"Manifest request to "</span> <span class="token operator">+</span> requestPath 
            <span class="token operator">+</span> <span class="token string">" failed."</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token punctuation">}</span><span class="token keyword">else</span><span class="token punctuation">{</span>
            <span class="token function">resolve</span><span class="token punctuation">(</span>JSON<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span>responseText<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span>update<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment" spellcheck="true">// {h: "dcc99b114b8c64461a2e", c: {5: true}}</span>
      <span class="token comment" spellcheck="true">// 新的hotUpdateHash</span>
      hotUpdateNewHash <span class="token operator">=</span> update<span class="token punctuation">.</span>h<span class="token punctuation">;</span>
      <span class="token comment" spellcheck="true">// 向文档插入 hot-update.js script </span>
      <span class="token function">hotEnsureUpdateChunk</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span></code></pre>
<h2 id="hotEnsureUpdateChunk-实现"><a href="#hotEnsureUpdateChunk-实现" class="headerlink" title="hotEnsureUpdateChunk 实现"></a>hotEnsureUpdateChunk 实现</h2><p>hotEnsureUpdateChunk 函数的逻辑是向 HTML 文档插入 hot-update.js script 脚本。 hotEnsureUpdateChunk 调用 hotDownloadUpdateChunk 函数</p>
<pre><code>function hotDownloadUpdateChunk(chunkId) { 
    var head = document.getElementsByTagName(&quot;head&quot;)[0];
    var script = document.createElement(&quot;script&quot;);
    script.type = &quot;text/javascript&quot;;
    script.charset = &quot;utf-8&quot;;
    script.src = __webpack_require__.p + &quot;&quot; + chunkId + &quot;.&quot; + hotCurrentHash + &quot;.hot-update.js&quot;;
    head.appendChild(script);
}</code></pre><h2 id="开启更新机制"><a href="#开启更新机制" class="headerlink" title="开启更新机制"></a>开启更新机制</h2><ul>
<li><p>开启热更新构建后， 每个 Vue 组件构建的代码都有下面这么一段 hotAPI 代码：</p>
<pre><code>/* hot reload */
Component.options.__file = &quot;app/web/page/about/about.vue&quot;
if (true) {(function () {
var hotAPI = __webpack_require__(3)
hotAPI.install(__webpack_require__(0), false)
if (!hotAPI.compatible) return
module.hot.accept()
if (!module.hot.data) {
  hotAPI.createRecord(&quot;data-v-aafed0d8&quot;, Component.options)
} else {
  hotAPI.reload(&quot;data-v-aafed0d8&quot;, Component.options)
&#39; + &#39;  }
module.hot.dispose(function (data) {
  disposed = true
})
})()}</code></pre></li>
<li><p>createRecord 和 reload 触发 UI 更新</p>
</li>
</ul>
<p>获取 Vue 组件的 render，重新 render 组件， 继而实现 UI 无刷新更新。</p>
<pre class=" language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">makeOptionsHot</span><span class="token punctuation">(</span>id<span class="token punctuation">,</span> options<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>options<span class="token punctuation">.</span>functional<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment" spellcheck="true">// 获取组件的render 方法，重新 render</span>
    <span class="token keyword">var</span> render <span class="token operator">=</span> options<span class="token punctuation">.</span>render
    options<span class="token punctuation">.</span>render <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>h<span class="token punctuation">,</span> ctx<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">var</span> instances <span class="token operator">=</span> map<span class="token punctuation">[</span>id<span class="token punctuation">]</span><span class="token punctuation">.</span>instances
      <span class="token keyword">if</span> <span class="token punctuation">(</span>instances<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>ctx<span class="token punctuation">.</span>parent<span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        instances<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>ctx<span class="token punctuation">.</span>parent<span class="token punctuation">)</span>
      <span class="token punctuation">}</span>
      <span class="token keyword">return</span> <span class="token function">render</span><span class="token punctuation">(</span>h<span class="token punctuation">,</span> ctx<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    <span class="token function">injectHook</span><span class="token punctuation">(</span>options<span class="token punctuation">,</span> <span class="token string">'beforeCreate'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">var</span> record <span class="token operator">=</span> map<span class="token punctuation">[</span>id<span class="token punctuation">]</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>record<span class="token punctuation">.</span>Ctor<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        record<span class="token punctuation">.</span>Ctor <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>constructor
      <span class="token punctuation">}</span>
      record<span class="token punctuation">.</span>instances<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token function">injectHook</span><span class="token punctuation">(</span>options<span class="token punctuation">,</span> <span class="token string">'beforeDestroy'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">var</span> instances <span class="token operator">=</span> map<span class="token punctuation">[</span>id<span class="token punctuation">]</span><span class="token punctuation">.</span>instances
      instances<span class="token punctuation">.</span><span class="token function">splice</span><span class="token punctuation">(</span>instances<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h2 id="热更新流程总结"><a href="#热更新流程总结" class="headerlink" title="热更新流程总结"></a>热更新流程总结</h2><p><img src="https://cdn.nlark.com/yuque/0/2019/jpeg/116733/1560615347798-2b0a4d7f-40a9-4ee5-b2c3-c38092e57929.jpeg#align=left&display=inline&height=873&originHeight=815&originWidth=720&size=0&status=done&width=771" alt=""></p>
<ol>
<li>Webpack编译期，为需要热更新的 entry 注入热更新代码(EventSource通信)</li>
<li>页面首次打开后，服务端与客户端通过 EventSource 建立通信渠道，把下一次的 hash 返回前端</li>
<li>客户端获取到hash，这个hash将作为下一次请求服务端 hot-update.js 和 hot-update.json的hash</li>
<li>修改页面代码后，Webpack 监听到文件修改后，开始编译，编译完成后，发送 build 消息给客户端</li>
<li>客户端获取到hash，成功后客户端构造hot-update.js script链接，然后插入主文档</li>
<li>hot-update.js 插入成功后，执行hotAPI 的 createRecord 和 reload方法，获取到 Vue 组件的 render方法，重新 render 组件， 继而实现 UI 无刷新更新。</li>
</ol>
<p>关键代码</p>
<ul>
<li><a href="https://link.zhihu.com/?target=https%3A//github.com/webpack/webpack-dev-middleware/tree/master/client.js">webpack-dev-middleware/client</a></li>
<li><a href="https://link.zhihu.com/?target=https%3A//github.com/webpack/webpack-dev-middleware/tree/master/middleware.js">webpack-dev-middleware/middleware</a></li>
<li><a href="https://link.zhihu.com/?target=https%3A//github.com/hubcarl/egg-vue-webpack-boilerplate/blob/master/app/web/page/about/about.vue">egg-vue-webpack-boilerplate</a></li>
<li><a href="https://link.zhihu.com/?target=https%3A//github.com/hubcarl/webpack-tool">webpack-dev-middleware 和 webpack-hot-middleware</a><a href="https://link.zhihu.com/?target=https%3A//github.com/hubcarl/webpack-tool">热更新集成</a></li>
</ul>
<p>知乎专栏：<a href="https://zhuanlan.zhihu.com/p/30623057" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/30623057</a></p>

                </div>
                <hr/>
    
                

    <div class="reprint" id="reprint-statement">
        
            <div class="reprint__author">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-user">
                        Author:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://easy-team.github.io" rel="external nofollow noreferrer">sky</a>
                </span>
            </div>
            <div class="reprint__type">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-link">
                        Link:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://easy-team.github.io/blog/qwr449/">https://easy-team.github.io/blog/qwr449/</a>
                </span>
            </div>
            <div class="reprint__type">
                    <span class="reprint-meta" style="font-weight: bold;">
                        <i class="fas fa-link">
                            yuque:
                        </i>
                    </span>
                    <span class="reprint-info">
                        <a href="https://yuque.com/easy-team/blog/qwr449/" target="_blank" rel="noopener">https://yuque.com/easy-team/blog/qwr449/</a>
                    </span>
            </div>
            <div class="reprint__notice">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-copyright">
                        Reprint policy:
                    </i>
                </span>
                <span class="reprint-info">
                    All articles in this blog are used except for special statements
                    <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>
                    reprint polocy. If reproduced, please indicate source
                    <a href="https://easy-team.github.io" target="_blank">sky</a>
                    !
                </span>
            </div>
        
    </div>

    <script async defer>
      document.addEventListener("copy", function (e) {
        let toastHTML = '<span>Copied successfully, please follow the reprint policy of this article</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">more</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }
    </script>


    
                <div class="tag_share" style="display: block; margin-top: 20px;">
                    <div class="post-meta__tag-list" style="display: inline-block;">
                        
                            
                        
                    </div>
                    <div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
                        <link rel="stylesheet" type="text/css" href="/libs/share/css/share.min.css">

<div id="article-share">
    
    
    <div class="social-share" data-sites="qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>
    <script src="/libs/share/js/social-share.min.js"></script>
    

    

</div>

                    </div>

                </div>
                
            </div>
        </div>
        
    
        
    
        
    
        
    
        
    
        

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">
                <i class="fas fa-chevron-left"></i>&nbsp;Previous</div>
            <div class="card">
                <a href="/blog/wk5ney/">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/19.jpg" class="responsive-img" alt="React Native 自定义插件">
                        
                        <span class="card-title">React Native 自定义插件</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            原文： http://hubcarl.github.io/blog/2016/08/13/react-native-plugin/编写自定义插件需要继承ReactContextBaseJavaModule和实现ReactPackage接口, 具体实现步骤如下：1. 继承ReactContext...
                        
                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <i class="far fa-clock fa-fw icon-date"></i>2019-10-20
                        </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-user fa-fw"></i>
                            sky
                            
                        </span>
                    </div>
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">
                Next&nbsp;<i class="fas fa-chevron-right"></i>
            </div>
            <div class="card">
                <a href="/blog/egret/">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/17.jpg" class="responsive-img" alt="Egret  游戏开发">
                        
                        <span class="card-title">Egret  游戏开发</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            命令行egret buildegret run -a   自动编译，浏览器不能自动刷新Stagepublic constructor() {    super();    this.once(egret.Event.ADDED_TO_STAGE, this.onAddToStage, this...
                        
                    </div>
                    <div class="publish-info">
                            <span class="publish-date">
                                <i class="far fa-clock fa-fw icon-date"></i>2019-10-20
                            </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-user fa-fw"></i>
                            sky
                            
                        </span>
                    </div>
                </div>
                
            </div>
        </div>
        
    </div>
</article>

     </div>
</div>


<script>
    $('#articleContent').on('copy', function (e) {
        // IE8 or earlier browser is 'undefined'
        if (typeof window.getSelection === 'undefined') return;

        var selection = window.getSelection();
        // if the selection is short let's not annoy our users.
        if (('' + selection).length < Number.parseInt('50')) {
            return;
        }

        // create a div outside of the visible area and fill it with the selected text.
        var bodyElement = document.getElementsByTagName('body')[0];
        var newdiv = document.createElement('div');
        newdiv.style.position = 'absolute';
        newdiv.style.left = '-99999px';
        bodyElement.appendChild(newdiv);
        newdiv.appendChild(selection.getRangeAt(0).cloneContents());

        // we need a <pre> tag workaround.
        // otherwise the text inside "pre" loses all the line breaks!
        if (selection.getRangeAt(0).commonAncestorContainer.nodeName === 'PRE') {
            newdiv.innerHTML = "<pre>" + newdiv.innerHTML + "</pre>";
        }

        var url = document.location.href;
        newdiv.innerHTML += '<br />'
            + 'From: easy-team<br />'
            + 'Author: sky<br />'
            + 'Link: <a href="' + url + '">' + url + '</a><br />'
            + '本文章著作权归作者所有，任何形式的转载都请注明出处。';

        selection.selectAllChildren(newdiv);
        window.setTimeout(function () {bodyElement.removeChild(newdiv);}, 200);
    });
</script>


<!-- 代码块功能依赖 -->

<script type="text/javascript" src="/libs/codeBlock/codeBlockFuction.js"></script>


<!-- 代码语言 -->

<script type="text/javascript" src="/libs/codeBlock/codeLang.js"></script>

    
<!-- 代码块复制 -->

<script type="text/javascript" src="/libs/codeBlock/codeCopy.js"></script>


<!-- 代码块收缩 -->

<script type="text/javascript" src="/libs/codeBlock/codeShrink.js"></script>


<!-- 代码块折行 -->

<style type="text/css">
code[class*="language-"], pre[class*="language-"] { white-space: pre !important; }
</style>

<!-- menu-selected -->
<script>
    var pathname = location.pathname.replace(/^\//, '').replace(/\/$/, '').replace(/\//g, '-');
    $('#m-nav-item-' + pathname).addClass('doc-left-nav-mene-selected');
</script>

    <footer class="page-footer bg-color">
    <div class="container row center-align">
        <div class="col s12 m8 l8 copy-right">
            Copyright&nbsp;&copy;
            <span id="year">年份</span>
            <a href="https://easy-team.github.io" target="_blank">sky</a>
            |&nbsp;Powered by&nbsp;<a href="https://hexo.io/" target="_blank">Hexo</a>
            |&nbsp;Theme&nbsp;<a href="https://github.com/easy-team" target="_blank">Matery</a>
            <br>
            
            &nbsp;<i class="fas fa-chart-area"></i>&nbsp;站点总字数:&nbsp;<span
                class="white-color">106.5k</span>&nbsp;字
            
            
            
            
            
            
            <span id="busuanzi_container_site_pv">
                |&nbsp;<i class="far fa-eye"></i>&nbsp;总访问量:&nbsp;<span id="busuanzi_value_site_pv"
                    class="white-color"></span>&nbsp;次
            </span>
            
            
            <span id="busuanzi_container_site_uv">
                |&nbsp;<i class="fas fa-users"></i>&nbsp;总访问人数:&nbsp;<span id="busuanzi_value_site_uv"
                    class="white-color"></span>&nbsp;人
            </span>
            
            <br>
            
            <span id="sitetime">载入运行时间...</span>
            <script>
                function siteTime() {
                    window.setTimeout("siteTime()", 1000);
                    var seconds = 1000;
                    var minutes = seconds * 60;
                    var hours = minutes * 60;
                    var days = hours * 24;
                    var years = days * 365;
                    var today = new Date();
                    var startYear = "2019";
                    var startMonth = "10";
                    var startDate = "20";
                    var startHour = "0";
                    var startMinute = "0";
                    var startSecond = "0";
                    var todayYear = today.getFullYear();
                    var todayMonth = today.getMonth() + 1;
                    var todayDate = today.getDate();
                    var todayHour = today.getHours();
                    var todayMinute = today.getMinutes();
                    var todaySecond = today.getSeconds();
                    var t1 = Date.UTC(startYear, startMonth, startDate, startHour, startMinute, startSecond);
                    var t2 = Date.UTC(todayYear, todayMonth, todayDate, todayHour, todayMinute, todaySecond);
                    var diff = t2 - t1;
                    var diffYears = Math.floor(diff / years);
                    var diffDays = Math.floor((diff / days) - diffYears * 365);
                    var diffHours = Math.floor((diff - (diffYears * 365 + diffDays) * days) / hours);
                    var diffMinutes = Math.floor((diff - (diffYears * 365 + diffDays) * days - diffHours * hours) /
                        minutes);
                    var diffSeconds = Math.floor((diff - (diffYears * 365 + diffDays) * days - diffHours * hours -
                        diffMinutes * minutes) / seconds);
                    if (startYear == todayYear) {
                        document.getElementById("year").innerHTML = todayYear;
                        document.getElementById("sitetime").innerHTML = "本站已安全运行 " + diffDays + " 天 " + diffHours +
                            " 小时 " + diffMinutes + " 分钟 " + diffSeconds + " 秒";
                    } else {
                        document.getElementById("year").innerHTML = startYear + " - " + todayYear;
                        document.getElementById("sitetime").innerHTML = "本站已安全运行 " + diffYears + " 年 " + diffDays +
                            " 天 " + diffHours + " 小时 " + diffMinutes + " 分钟 " + diffSeconds + " 秒";
                    }
                }
                setInterval(siteTime, 1000);
            </script>
            
            <br>
            
        </div>
        <div class="col s12 m4 l4 social-link social-statis">
    <a href="https://github.com/hubcarl" class="tooltipped" target="_blank" data-tooltip="访问我的GitHub" data-position="top" data-delay="50">
        <i class="fab fa-github"></i>
    </a>



    <a href="mailto:hubcarl@126.com" class="tooltipped" target="_blank" data-tooltip="邮件联系我" data-position="top" data-delay="50">
        <i class="fas fa-envelope-open"></i>
    </a>







    <a href="tencent://AddContact/?fromId=50&fromSubId=1&subcmd=all&uin=433207205" class="tooltipped" target="_blank" data-tooltip="QQ联系我: 433207205" data-position="top" data-delay="50">
        <i class="fab fa-qq"></i>
    </a>



    <a href="https://weibo.com/hubcarl" class="tooltipped" target="_blank" data-tooltip="关注我的微博: https://weibo.com/hubcarl" data-position="top" data-delay="50">
        <i class="fab fa-weibo"></i>
    </a>



    <a href="https://zhuanlan.zhihu.com/easywebpack" class="tooltipped" target="_blank" data-tooltip="关注我的知乎: https://zhuanlan.zhihu.com/easywebpack" data-position="top" data-delay="50">
        <i class="fab fa-zhihu1">知</i>
    </a>



    <a href="/atom.xml" class="tooltipped" target="_blank" data-tooltip="RSS 订阅" data-position="top" data-delay="50">
        <i class="fas fa-rss"></i>
    </a>

</div>
    </div>
</footer>

<div class="progress-bar"></div>


    <!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;Search</span>
            <input type="search" id="searchInput" name="s" placeholder="Please enter a search keyword"
                   class="search-input">
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script src="/js/search.js"></script>
<script type="text/javascript">
$(function () {
    searchFunc("/" + "search.xml", 'searchInput', 'searchResult');
});
</script>
    <!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fas fa-arrow-up"></i>
    </a>
</div>


    

    <!-- Global site tag (gtag.js) - Google Analytics -->


    <!-- Baidu Analytics -->


    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        bp.async = true;
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    
    <script async src="/libs/others/busuanzi.pure.mini.js"></script>
    

    

    

    

    

    
    <script type="text/javascript" src="/libs/background/ribbon-dynamic.js" async="async"></script>
    
    
    
    <script src="/libs/instantpage/instantpage.js" type="module"></script>
    

</body>

</html>
